<?php

namespace App\Traits;

use App\Events\ApprovalReviewed;
use App\Models\ApprovalFlow;
use App\Models\FlowNode;

/**
 * Trait CanBeApprove.
 */
trait CanBeApprove
{
    public static function bootCanBeApprove()
    {
        static::created(function ($model) {
            $flow = ApprovalFlow::query()->where('type', $model->getMorphClass())->first();

            if (!$flow) {
                return;
            }

            $model->flows()->attach($flow);
        });
    }

    /**
     * 一个模型只能绑定一个 flow
     *
     * @return \Illuminate\Database\Eloquent\Relations\MorphOne
     */
    public function flows()
    {
        return $this
            ->morphToMany(
                ApprovalFlow::class,
                'approvable',
                'approvables',
                'approvable_id',
                'flow_id',
            )
            ->withTimestamps()
            ->withPivot('status')
            ->withTrashed();
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
     */
    public function records()
    {
        return $this
            ->morphToMany(
                FlowNode::class,
                'approvable',
                'flow_records',
                'approvable_id',
                'node_id',
            )
            ->withTimestamps()
            ->withPivot([
                'flow_id',
                'reviewer_id',
                'reviewed_at',
                'status',
            ]);
    }

    /**
     * @param \App\Models\ApprovalFlow $approvalFlow
     * @param string                   $status
     * @param string|null              $remark
     */
    public function review(ApprovalFlow $approvalFlow, string $status, string $remark = null)
    {
        $records = $this
            ->records()
            ->where('flow_nodes.flow_id', $approvalFlow->id)
            ->get();

        if (!$records->isEmpty()) {
            $nodes = $approvalFlow->nodes()->orderBy('weight')->get();

            foreach ($nodes as $node) {
                $record = $records->where('node_id', $node->id)->first();

                if (!$record || \in_array($record->status, [ApprovalFlow::STATUS_HANGED_UP, ApprovalFlow::STATUS_REJECTED])) {
                    $checkNode = $node;
                    break;
                }
            }

            \abort_if(!isset($checkNode), 403, '不可重复审核！');
        } else {
            $checkNode = $approvalFlow->nodes()->orderBy('weight')->first();

            $approvableStatus = ApprovalFlow::APPROVAL_STATUS_PROCESSING;

            if ($approvalFlow->nodes()->count() === 1 && $status === ApprovalFlow::STATUS_PASSED) {
                $approvableStatus = ApprovalFlow::APPROVAL_STATUS_PASSED;
            } elseif ($status === ApprovalFlow::STATUS_REJECTED) {
                $approvableStatus = ApprovalFlow::APPROVAL_STATUS_REJECTED;
            }

            $this->flows()->updateExistingPivot($approvalFlow->id, [
               'status' => $approvableStatus,
            ]);

            $approvable = $this;

            \event(new ApprovalReviewed($approvable, $approvableStatus));
        }

        \abort_if(!$checkNode || !$checkNode->canBeReview(\auth()->user()), 403);

        $this->records()->attach([
            $checkNode->id => [
                'flow_id' => $approvalFlow->id,
                'node_id' => $checkNode->id,
                'reviewer_id' => \auth()->id(),
                'reviewed_at' => \now(),
                'status' => $status,
            ]
        ]);
    }
}
